/*++

Copyright (c) 1989  Microsoft Corporation

Module Name:

   allocpag.c

Abstract:

    This module contains the routines which allocate and deallocate
    one or more pages from paged or nonpaged pool.

Author:

    Lou Perazzoli (loup) 6-Apr-1989

Revision History:

--*/

#include "mi.h"

//
// HACKHACK: ignore this warning for now. This will be fixed later when
//           this file is updated to NT 5.
//

#pragma warning(disable:4700)

#ifdef ALLOC_PRAGMA
#pragma alloc_text(INIT,MiInitializeNonPagedPool)
#if DBG || (i386 && !FPO)
#pragma alloc_text(PAGELK, MmSnapShotPool)
#endif // DBG || (i386 && !FPO)
#endif

ULONG MmPagedPoolHint;

ULONG MmPagedPoolCommit;

ULONG MmAllocatedPagedPool;

ULONG MmAllocatedNonPagedPool;

PVOID MmNonPagedPoolExpansionStart;

LIST_ENTRY MmNonPagedPoolFreeListHead;

extern ULONG MmSystemPageDirectory;

extern POOL_DESCRIPTOR NonPagedPoolDescriptor;

#define MM_SMALL_ALLOCATIONS 4


POOL_TYPE
MmDeterminePoolType (
    IN PVOID VirtualAddress
    )

/*++

Routine Description:

    This function determines which pool a virtual address resides within.

Arguments:

    VirtualAddress - Supplies the virtual address to determine which pool
                     it resides within.

Return Value:

    Returns the POOL_TYPE (PagedPool or NonPagedPool), it never returns
            any information about MustSucceed pool types.

Environment:

    Kernel Mode Only.

--*/

{
    if ((VirtualAddress >= MmPagedPoolStart) &&
        (VirtualAddress <= MmPagedPoolEnd)) {
        return PagedPool;
    }
    return NonPagedPool;
}


PVOID
MiAllocatePoolPages (
    IN POOL_TYPE PoolType,
    IN ULONG SizeInBytes
    )

/*++

Routine Description:

    This function allocates a set of pages from the specified pool
    and returns the starting virtual address to the caller.

    For the NonPagedPoolMustSucceed case, the caller must first
    attempt to get NonPagedPool and if and ONLY IF that fails, then
    MiAllocatePoolPages should be called again with the PoolType of
    NonPagedPoolMustSucceed.

Arguments:

    PoolType - Supplies the type of pool from which to obtain pages.

    SizeInBytes - Supplies the size of the request in bytes.  The actual
                  size returned is rounded up to a page boundary.

Return Value:

    Returns a pointer to the allocated pool, or NULL if no more pool is
    available.

Environment:

    These functions are used by the general pool allocation routines
    and should not be called directly.

    Mutexes guarding the pool databases must be held when calling
    these functions.

    Kernel mode, IRQP at DISPATCH_LEVEL.

--*/

{
    ULONG SizeInPages;
    ULONG StartPosition;
    ULONG EndPosition;
    PMMPTE StartingPte;
    PMMPTE PointerPte;
    PMMPFN Pfn1;
    MMPTE TempPte;
    ULONG PageFrameIndex;
    PVOID BaseVa;
    KIRQL OldIrql;
    ULONG i;
    PLIST_ENTRY Entry;
    PMMFREE_POOL_ENTRY FreePageInfo;

    SizeInPages = BYTES_TO_PAGES (SizeInBytes);

    ASSERT (SizeInPages < 10000);

    if (PoolType == NonPagedPoolMustSucceed) {

        //
        // Pool expansion failed, see if any Must Succeed
        // pool is still left.
        //

        if (MmNonPagedMustSucceed == NULL) {

            //
            // No more pool exists.  Bug Check.
            //

            KeBugCheckEx (MUST_SUCCEED_POOL_EMPTY,
                          SizeInBytes,
                          NonPagedPoolDescriptor.TotalPages,
                          NonPagedPoolDescriptor.TotalBigPages,
                          MmAvailablePages);
        }

        //
        // Remove a page from the must succeed pool.
        //

        ASSERT (SizeInBytes <= PAGE_SIZE);

        BaseVa = MmNonPagedMustSucceed;

        MmNonPagedMustSucceed = (PVOID)(*(PULONG)BaseVa);
        return BaseVa;
    }

    if (PoolType == NonPagedPool) {

        //
        // NonPaged pool is linked together through the pages themselves.
        //

        Entry = MmNonPagedPoolFreeListHead.Flink;

        while (Entry != &MmNonPagedPoolFreeListHead) {

            //
            // The list is not empty, see if this one has enough
            // space.
            //

            FreePageInfo = CONTAINING_RECORD(Entry,
                                             MMFREE_POOL_ENTRY,
                                             List);

            ASSERT (FreePageInfo->Signature == MM_FREE_POOL_SIGNATURE);
            if (FreePageInfo->Size >= SizeInPages) {

                //
                // This entry has sufficient space, remove
                // the pages from the end of the allocation.
                //

                FreePageInfo->Size -= SizeInPages;

                if (FreePageInfo->Size == 0) {
                    RemoveEntryList (&FreePageInfo->List);
                }

                //
                // Adjust the number of free pages remaining in the pool.
                //

                MmNumberOfFreeNonPagedPool -= SizeInPages;
                ASSERT ((LONG)MmNumberOfFreeNonPagedPool >= 0);

                BaseVa = (PVOID)((PCHAR)FreePageInfo +
                                        (FreePageInfo->Size  << PAGE_SHIFT));

                //
                // Mark start and end of allocation in the PFN database.
                //

                if (MI_IS_PHYSICAL_ADDRESS(BaseVa)) {

                    //
                    // On certains architectures (e.g., MIPS) virtual addresses
                    // may be physical and hence have no corresponding PTE.
                    //

                    PageFrameIndex = MI_CONVERT_PHYSICAL_TO_PFN (BaseVa);
                } else {
                    PointerPte = MiGetPteAddress(BaseVa);
                    ASSERT (PointerPte->u.Hard.Valid == 1);
                    PageFrameIndex = PointerPte->u.Hard.PageFrameNumber;
                }
                Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);

                ASSERT (Pfn1->u3.e1.StartOfAllocation == 0);
                Pfn1->u3.e1.StartOfAllocation = 1;

                //
                // Calculate the ending PTE's address.
                //

                if (SizeInPages != 1) {

                    if (MI_IS_PHYSICAL_ADDRESS(BaseVa)) {
                        Pfn1 += SizeInPages - 1;
                    } else {
                        PointerPte += SizeInPages - 1;
                        ASSERT (PointerPte->u.Hard.Valid == 1);
                        Pfn1 = MI_PFN_ELEMENT (PointerPte->u.Hard.PageFrameNumber);
                    }

                } else {

#if defined(_ALPHA_)

                   //
                   // See if KSEG0 can be used to map this.
                   //

                   if  ((BaseVa > (PVOID)KSEG2_BASE) &&
                        (PageFrameIndex < MmSubsectionTopPage)) {
                       BaseVa = (PVOID)(KSEG0_BASE + (PageFrameIndex << PAGE_SHIFT));
                   }
#endif //ALPHA

#if defined(_MIPS_)

                   //
                   // See if KSEG0 can be used to map this.
                   //

                   if  ((BaseVa > (PVOID)KSEG1_BASE) &&
                        (MI_GET_PAGE_COLOR_FROM_VA (BaseVa) ==
                           (MM_COLOR_MASK & PageFrameIndex)) &&
                       (PageFrameIndex < MmSubsectionTopPage)) {
                       BaseVa = (PVOID)(KSEG0_BASE + (PageFrameIndex << PAGE_SHIFT));
                   }
#endif //MIPS

#if defined(_X86_)

                   //
                   // See if KSEG0 can be used to map this.
                   //

                   if  ((BaseVa > (PVOID)MM_KSEG2_BASE) &&
                        (PageFrameIndex < MmSubsectionTopPage)) {
                       BaseVa = (PVOID)(MM_KSEG0_BASE + (PageFrameIndex << PAGE_SHIFT));
                   }
#endif //X86

                NOTHING;

                }
                ASSERT (Pfn1->u3.e1.EndOfAllocation == 0);
                Pfn1->u3.e1.EndOfAllocation = 1;

                MmAllocatedNonPagedPool += SizeInPages;
                return BaseVa;
            }
            Entry = FreePageInfo->List.Flink;
        }

        //
        // No more entries on the list, expand nonpaged pool if
        // possible to satisfy this request.
        //

        //
        // Check to see if there are too many unused segments laying
        // around, and if so, set an event so they get deleted.
        //

        if (MmUnusedSegmentCount > MmUnusedSegmentCountMaximum) {
            KeSetEvent (&MmUnusedSegmentCleanup, 0, FALSE);
        }

        LOCK_PFN2 (OldIrql);

        //
        // Make sure we have 1 more than the number of pages
        // requested available.
        //

        if (MmAvailablePages <= SizeInPages) {

            UNLOCK_PFN2 (OldIrql);

            //
            // There are free physical pages to expand
            // nonpaged pool.
            //

            return NULL;
        }

        //
        // Try to find system ptes to expand the pool into.
        //

        StartingPte = MiReserveSystemPtes (SizeInPages,
                                           NonPagedPoolExpansion,
                                           0,
                                           0,
                                           FALSE);

        if (StartingPte == NULL) {

            UNLOCK_PFN2 (OldIrql);

            //
            // There are no free physical PTEs to expand
            // nonpaged pool.
            //

            return NULL;
        }

        //
        // Update the count of available resident pages.
        //

        MmResidentAvailablePages -= SizeInPages;

        //
        // Charge commitment as non paged pool uses physical memory.
        //

        MiChargeCommitmentCantExpand (SizeInPages, TRUE);

        //
        //  Expand the pool.
        //

        PointerPte = StartingPte;
        TempPte = ValidKernelPte;
        MmAllocatedNonPagedPool += SizeInPages;
        i= SizeInPages;

        do {
            PageFrameIndex = MiRemoveAnyPage (
                                MI_GET_PAGE_COLOR_FROM_PTE (PointerPte));

            Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);

            Pfn1->u3.e2.ReferenceCount = 1;
            Pfn1->u2.ShareCount = 1;
            Pfn1->PteAddress = PointerPte;
            Pfn1->OriginalPte.u.Long = MM_DEMAND_ZERO_WRITE_PTE;
            Pfn1->PteFrame = MiGetPteAddress(PointerPte)->u.Hard.PageFrameNumber;
            Pfn1->u3.e1.PageLocation = ActiveAndValid;

            TempPte.u.Hard.PageFrameNumber = PageFrameIndex;
            *PointerPte = TempPte;
            PointerPte += 1;
            SizeInPages -= 1;
        } while (SizeInPages > 0);

        Pfn1->u3.e1.EndOfAllocation = 1;
        Pfn1 = MI_PFN_ELEMENT (StartingPte->u.Hard.PageFrameNumber);
        Pfn1->u3.e1.StartOfAllocation = 1;

        UNLOCK_PFN2 (OldIrql);

        BaseVa = MiGetVirtualAddressMappedByPte (StartingPte);

#if defined(_ALPHA_)
        if (i == 1) {

            //
            // See if KSEG0 can be used to map this.
            //

            if (PageFrameIndex < MmSubsectionTopPage) {
                 BaseVa = (PVOID)(KSEG0_BASE + (PageFrameIndex << PAGE_SHIFT));
            }
        }
#endif //ALPHA

#if defined(_MIPS_)
        if (i == 1) {

            //
            // See if KSEG0 can be used to map this.
            //

            if ((MI_GET_PAGE_COLOR_FROM_VA (BaseVa) ==
                    (MM_COLOR_MASK & PageFrameIndex)) &&
                (PageFrameIndex < MmSubsectionTopPage)) {
                BaseVa = (PVOID)(KSEG0_BASE + (PageFrameIndex << PAGE_SHIFT));
            }
        }
#endif //MIPS

#if defined(_X86_)
       if (i == 1) {
            //
            // See if KSEG0 can be used to map this.
            //

            if (PageFrameIndex < MmSubsectionTopPage) {
                BaseVa = (PVOID)(MM_KSEG0_BASE + (PageFrameIndex << PAGE_SHIFT));
            }
       }
#endif //X86

        return BaseVa;
    }

    //
    // Paged Pool.
    //

    StartPosition = RtlFindClearBitsAndSet (
                               MmPagedPoolAllocationMap,
                               SizeInPages,
                               MmPagedPoolHint
                               );

    if ((StartPosition == 0xFFFFFFFF) &&
        (MmPagedPoolHint != 0)) {

        if (MmUnusedSegmentCount > MmUnusedSegmentCountMaximum) {
            KeSetEvent (&MmUnusedSegmentCleanup, 0, FALSE);
        }

        //
        // No free bits were found, check from the start of
        // the bit map.

        StartPosition = RtlFindClearBitsAndSet (
                                   MmPagedPoolAllocationMap,
                                   SizeInPages,
                                   0
                                   );
    }

    //
    // If start position = -1, no room in pool.  Attempt to
    // expand NonPagedPool.
    //

    if (StartPosition == 0xFFFFFFFF) {


        //
        // Attempt to expand the paged pool.
        //

        StartPosition = ((SizeInPages - 1) / PTE_PER_PAGE) + 1;

        //
        // Make sure there are enough space to create the prototype PTEs.
        //

        if (((StartPosition - 1) + MmNextPteForPagedPoolExpansion) >
            MiGetPteAddress (MmLastPteForPagedPool)) {

            //
            // Can't expand pool any more.
            //

            return NULL;
        }

        LOCK_PFN (OldIrql);

        //
        // Make sure we have 1 more than the number of pages
        // requested available.
        //

        if (MmAvailablePages <= StartPosition) {

            UNLOCK_PFN (OldIrql);

            //
            // There are free physical pages to expand
            // paged pool.
            //

            return NULL;
        }

        //
        // Update the count of available resident pages.
        //

        MmResidentAvailablePages -= StartPosition;

        //
        //  Expand the pool.
        //

        EndPosition = (MmNextPteForPagedPoolExpansion -
                          MiGetPteAddress(MmFirstPteForPagedPool)) *
                          PTE_PER_PAGE;

        RtlClearBits (MmPagedPoolAllocationMap,
                      EndPosition,
                      StartPosition * PTE_PER_PAGE);

        PointerPte = MmNextPteForPagedPoolExpansion;
        StartingPte =
                (PMMPTE)MiGetVirtualAddressMappedByPte(PointerPte);
        MmNextPteForPagedPoolExpansion += StartPosition;

        TempPte = ValidKernelPde;

        do {
            ASSERT (PointerPte->u.Hard.Valid == 0);

            MiChargeCommitmentCantExpand (1, TRUE);
            PageFrameIndex = MiRemoveAnyPage (
                                MI_GET_PAGE_COLOR_FROM_PTE (PointerPte));
            TempPte.u.Hard.PageFrameNumber = PageFrameIndex;
            *PointerPte = TempPte;

            //
            // Map valid PDE into system address space as well.
            //

            MmSystemPagePtes [((ULONG)PointerPte &
                    ((sizeof(MMPTE) * PDE_PER_PAGE) - 1)) / sizeof(MMPTE)] =
                                     TempPte;

            MiInitializePfnForOtherProcess (PageFrameIndex,
                                            PointerPte,
                                            MmSystemPageDirectory);

            RtlFillMemoryUlong (StartingPte,
                                PAGE_SIZE,
                                MM_KERNEL_DEMAND_ZERO_PTE);

            PointerPte += 1;
            StartingPte += PAGE_SIZE / sizeof(MMPTE);
            StartPosition -= 1;
        } while (StartPosition > 0);

        UNLOCK_PFN (OldIrql);

        StartPosition = RtlFindClearBitsAndSet (
                                   MmPagedPoolAllocationMap,
                                   SizeInPages,
                                   EndPosition
                                   );
        ASSERT (StartPosition != 0xffffffff);
    }
    MmPagedPoolHint = StartPosition + SizeInPages - 1;

    BaseVa = (PVOID)((PUCHAR)MmPageAlignedPoolBase[PoolType] +
                            (StartPosition * PAGE_SIZE));

    //
    // This is paged pool, the start and end can't be saved
    // in the PFN database as the page isn't always resident
    // in memory.  The ideal place to save the start and end
    // would be in the prototype PTE, but there are no free
    // bits.  To solve this problem, a bitmap which parallels
    // the allocation bitmap exists which contains set bits
    // in the positions where an allocation ends.  This
    // allows pages to be deallocated with only their starting
    // address.
    //
    // For sanity's sake, the starting address can be verified
    // from the 2 bitmaps as well.  If the page before the starting
    // address is not allocated (bit is zero in allocation bitmap)
    // then this page is obviously a start of an allocation block.
    // If the page before is allocated and the other bit map does
    // not indicate the previous page is the end of an allocation,
    // then the starting address is wrong and a bug check should
    // be issued.
    //

    try {

        MiChargeCommitmentCantExpand (SizeInPages, FALSE);
    } except (EXCEPTION_EXECUTE_HANDLER) {

        RtlClearBits (MmPagedPoolAllocationMap,
                      StartPosition,
                      SizeInPages);

        //
        // Could not commit the page, return NULL indicating
        // no pool was allocated.
        //

        return(NULL);
    }

    MmPagedPoolCommit += SizeInPages;
    EndPosition = StartPosition + SizeInPages - 1;
    RtlSetBits (MmEndOfPagedPoolBitmap, EndPosition, 1L);

    MmAllocatedPagedPool += SizeInPages;
    return BaseVa;
}

ULONG
MiFreePoolPages (
    IN PVOID StartingAddress
    )

/*++

Routine Description:

    This function returns a set of pages back to the pool from
    which they were obtained.  Once the pages have been deallocated
    the region provided by the allocation becomes available for
    allocation to other callers, i.e. any data in the region is now
    trashed and cannot be referenced.

Arguments:

    StartingAddress - Supplies the starting address which was returned
                      in a previous call to VmAllocatePages.

Return Value:

    Returns the number of pages deallocated.

Environment:

    These functions are used by the general pool allocation routines
    and should not be called directly.

    Mutexes guarding the pool databases must be held when calling
    these functions.

--*/

{
    ULONG StartPosition;
    ULONG i;
    ULONG NumberOfPages = 1;
    POOL_TYPE PoolType;
    PMMPTE PointerPte;
    PMMPFN Pfn1;
    ULONG PageFrameIndex;
    KIRQL OldIrql;
    PMMFREE_POOL_ENTRY Entry;
    PMMFREE_POOL_ENTRY NextEntry;

    //
    // Determine Pool type base on the virtual address of the block
    // to deallocate.
    //
    // This assumes NonPagedPool starts at a higher virtual address
    // then PagedPool.
    //

    if ((StartingAddress >= MmPagedPoolStart) &&
        (StartingAddress <= MmPagedPoolEnd)) {
        PoolType = PagedPool;
    } else {
        PoolType = NonPagedPool;
    }

    StartPosition = ((ULONG)StartingAddress -
                      (ULONG)MmPageAlignedPoolBase[PoolType]) >> PAGE_SHIFT;

    //
    // Check to insure this page is really a start of allocation.
    //

    if (PoolType == NonPagedPool) {

        if (StartPosition < MmMustSucceedPoolBitPosition) {

            PULONG NextList;

            //
            // This is must succeed pool, don't free it, just
            // add it to the front of the list.
            //
            // Note - only a single page can be released at a time.
            //

            NextList = (PULONG)StartingAddress;
            *NextList = (ULONG)MmNonPagedMustSucceed;
            MmNonPagedMustSucceed = StartingAddress;
            return NumberOfPages;
        }

        if (MI_IS_PHYSICAL_ADDRESS (StartingAddress)) {

            //
            // On certains architectures (e.g., MIPS) virtual addresses
            // may be physical and hence have no corresponding PTE.
            //

            Pfn1 = MI_PFN_ELEMENT (MI_CONVERT_PHYSICAL_TO_PFN (StartingAddress));
            if (StartPosition >= MmExpandedPoolBitPosition) {
                PointerPte = Pfn1->PteAddress;
                StartingAddress = MiGetVirtualAddressMappedByPte (PointerPte);
            }
        } else {
            PointerPte = MiGetPteAddress (StartingAddress);
            Pfn1 = MI_PFN_ELEMENT (PointerPte->u.Hard.PageFrameNumber);
        }

        ASSERT (Pfn1->u3.e1.StartOfAllocation != 0);
        Pfn1->u3.e1.StartOfAllocation = 0;

#if DBG
        if ((Pfn1->u3.e2.ReferenceCount > 1) &&
            (Pfn1->u3.e1.WriteInProgress == 0)) {
            DbgPrint ("MM:ALLOCPAGE - deleting pool locked for I/O %lx\n",
                 PageFrameIndex);
            ASSERT (Pfn1->u3.e2.ReferenceCount == 1);
        }
#endif //DBG

        //
        // Find end of allocation and release the pages.
        //

        while (Pfn1->u3.e1.EndOfAllocation == 0) {
            if (MI_IS_PHYSICAL_ADDRESS(StartingAddress)) {
                Pfn1 += 1;
            } else {
                PointerPte++;
                Pfn1 = MI_PFN_ELEMENT (PointerPte->u.Hard.PageFrameNumber);
            }
            NumberOfPages++;
#if DBG
            if ((Pfn1->u3.e2.ReferenceCount > 1) &&
                (Pfn1->u3.e1.WriteInProgress == 0)) {
                DbgPrint ("MM:ALLOCPAGE - deleting pool locked for I/O %lx\n",
                     PageFrameIndex);
                ASSERT (Pfn1->u3.e2.ReferenceCount == 1);
            }
#endif //DBG
        }

        MmAllocatedNonPagedPool -= NumberOfPages;

        Pfn1->u3.e1.EndOfAllocation = 0;
#if DBG
        RtlFillMemoryUlong (StartingAddress,
                            PAGE_SIZE * NumberOfPages,
                            0x23456789);
#endif //DBG

        if (StartingAddress > MmNonPagedPoolExpansionStart) {

            //
            // This page was from the expanded pool, should
            // it be freed?
            //
            // NOTE: all pages in the expanded pool area have PTEs
            // so no physical address checks need to be performed.
            //

            if ((NumberOfPages > 3) || (MmNumberOfFreeNonPagedPool > 5)) {

                //
                // Free these pages back to the free page list.
                //

                MI_MAKING_MULTIPLE_PTES_INVALID (TRUE);

                PointerPte = MiGetPteAddress (StartingAddress);

                //
                // Return commitment.
                //

                MiReturnCommitment (NumberOfPages);

                LOCK_PFN2 (OldIrql);

                for (i=0; i < NumberOfPages; i++) {

                    PageFrameIndex = PointerPte->u.Hard.PageFrameNumber;

                    //
                    // Set the pointer to PTE as empty so the page
                    // is deleted when the reference count goes to zero.
                    //

                    Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);
                    ASSERT (Pfn1->u2.ShareCount == 1);
                    Pfn1->u2.ShareCount = 0;
                    MI_SET_PFN_DELETED (Pfn1);
#if DBG
                    Pfn1->u3.e1.PageLocation = StandbyPageList;
#endif //DBG
                    MiDecrementReferenceCount (PageFrameIndex);

                    (VOID)KeFlushSingleTb (StartingAddress,
                                           TRUE,
                                           TRUE,
                                           (PHARDWARE_PTE)PointerPte,
                                           ZeroKernelPte.u.Flush);
                    StartingAddress = (PVOID)((ULONG)StartingAddress +
                                                                    PAGE_SIZE);
                    PointerPte += 1;
                }

                //
                // Update the count of available resident pages.
                //

                MmResidentAvailablePages += NumberOfPages;

                UNLOCK_PFN2(OldIrql);

                PointerPte -= NumberOfPages;

                MiReleaseSystemPtes (PointerPte,
                                     NumberOfPages,
                                     NonPagedPoolExpansion);

                return NumberOfPages;
            }
        }

        //
        // Add the pages to the list of free pages.
        //

        MmNumberOfFreeNonPagedPool += NumberOfPages;

        //
        // Check to see if the next allocation is free.
        //

        i = NumberOfPages;

        if (MI_IS_PHYSICAL_ADDRESS(StartingAddress)) {
            Pfn1 += 1;
        } else {
            PointerPte += 1;
            if (PointerPte->u.Hard.Valid == 1) {
                Pfn1 = MI_PFN_ELEMENT (PointerPte->u.Hard.PageFrameNumber);
            } else {
                Pfn1 = NULL;
            }
        }

        if (Pfn1 != NULL) {
            if (Pfn1->u3.e1.StartOfAllocation == 0) {

                //
                // This range of pages is free.  Remove this entry
                // from the list and add these pages to the current
                // range being freed.
                //

                Entry = (PMMFREE_POOL_ENTRY)((PCHAR)StartingAddress
                                            + (NumberOfPages << PAGE_SHIFT));
                ASSERT (Entry->Signature == MM_FREE_POOL_SIGNATURE);
                ASSERT (Entry->Owner == Entry);
#if DBG
                {
                    PMMPTE DebugPte;
                    PMMPFN DebugPfn;
                    if (MI_IS_PHYSICAL_ADDRESS(StartingAddress)) {

                        //
                        // On certains architectures (e.g., MIPS) virtual addresses
                        // may be physical and hence have no corresponding PTE.
                        //

                        DebugPfn = MI_PFN_ELEMENT (MI_CONVERT_PHYSICAL_TO_PFN (Entry));
                        DebugPfn += Entry->Size;
                        ASSERT (DebugPfn->u3.e1.StartOfAllocation == 1);
                    } else {
                        DebugPte = PointerPte + Entry->Size;
                        if (DebugPte->u.Hard.Valid == 1) {
                            DebugPfn = MI_PFN_ELEMENT (DebugPte->u.Hard.PageFrameNumber);
                            ASSERT (DebugPfn->u3.e1.StartOfAllocation == 1);
                        }
                    }
                }
#endif //DBG

                i += Entry->Size;
                RemoveEntryList (&Entry->List);
            }
        }

        //
        // Check to see if the previous page is the end of an allocation.
        // If it is not then end of an allocation, it must be free and
        // therefore this allocation can be tagged onto the end of
        // that allocation.
        //

        Entry = (PMMFREE_POOL_ENTRY)StartingAddress;

        if (MI_IS_PHYSICAL_ADDRESS(StartingAddress)) {
            Pfn1 = MI_PFN_ELEMENT (MI_CONVERT_PHYSICAL_TO_PFN (
                                        (PVOID)((PCHAR)Entry - PAGE_SIZE)));
        } else {
            PointerPte -= NumberOfPages + 1;
            if (PointerPte->u.Hard.Valid == 1) {
                Pfn1 = MI_PFN_ELEMENT (PointerPte->u.Hard.PageFrameNumber);
            } else {
                Pfn1 = NULL;
            }
        }
        if (Pfn1 != NULL) {
            if (Pfn1->u3.e1.EndOfAllocation == 0) {

                //
                // This range of pages is free, add these pages to
                // this entry.  The owner field points to the address
                // of the list entry which is linked into the free pool
                // pages list.
                //

                Entry = (PMMFREE_POOL_ENTRY)((PCHAR)StartingAddress - PAGE_SIZE);
                ASSERT (Entry->Signature == MM_FREE_POOL_SIGNATURE);
                Entry = Entry->Owner;
                ASSERT (Entry->Owner == Entry);

                //
                // If this entry became larger than MM_SMALL_ALLOCATIONS
                // pages, move it to the tail of the list.  This keeps the
                // small allocations at the front of the list.
                //

                if ((Entry->Size < MM_SMALL_ALLOCATIONS) &&
                    (Entry->Size + i) >= MM_SMALL_ALLOCATIONS) {

                    RemoveEntryList (&Entry->List);
                    InsertTailList (&MmNonPagedPoolFreeListHead, &Entry->List);
                }

                //
                // Add these pages to the previous entry.
                //

                Entry->Size += i;
            }
        }

        if (Entry == (PMMFREE_POOL_ENTRY)StartingAddress) {

            //
            // This entry was not combined with the previous, insert it
            // into the list.
            //

            Entry->Size = i;
            if (Entry->Size < MM_SMALL_ALLOCATIONS) {

                //
                // Small number of pages, insert this at the head of the list.
                //

                InsertHeadList (&MmNonPagedPoolFreeListHead, &Entry->List);
            } else {
                InsertTailList (&MmNonPagedPoolFreeListHead, &Entry->List);
            }
        }

        //
        // Set the owner field in all these pages.
        //

        NextEntry = (PMMFREE_POOL_ENTRY)StartingAddress;
        while (i > 0) {
            NextEntry->Owner = Entry;
#if DBG
            NextEntry->Signature = MM_FREE_POOL_SIGNATURE;
#endif

            NextEntry = (PMMFREE_POOL_ENTRY)((PCHAR)NextEntry + PAGE_SIZE);
            i -= 1;
        }

#if DBG
        NextEntry = Entry;
        for (i=0;i<Entry->Size ;i++ ) {
            {
                PMMPTE DebugPte;
                PMMPFN DebugPfn;
                if (MI_IS_PHYSICAL_ADDRESS(StartingAddress)) {

                    //
                    // On certains architectures (e.g., MIPS) virtual addresses
                    // may be physical and hence have no corresponding PTE.
                    //

                    DebugPfn = MI_PFN_ELEMENT (MI_CONVERT_PHYSICAL_TO_PFN (NextEntry));
                } else {

                    DebugPte = MiGetPteAddress (NextEntry);
                    DebugPfn = MI_PFN_ELEMENT (DebugPte->u.Hard.PageFrameNumber);
                }
                ASSERT (DebugPfn->u3.e1.StartOfAllocation == 0);
                ASSERT (DebugPfn->u3.e1.EndOfAllocation == 0);
                ASSERT (NextEntry->Owner == Entry);
                NextEntry = (PMMFREE_POOL_ENTRY)((PCHAR)NextEntry + PAGE_SIZE);
            }
        }
#endif

        return NumberOfPages;

    } else {

        //
        // Paged pool.  Need to verify start of allocation using
        // end of allocation bitmap.
        //

        ASSERT (RtlCheckBit (MmPagedPoolAllocationMap, StartPosition));

#if DBG
        if (StartPosition > 0) {
            if (RtlCheckBit (MmPagedPoolAllocationMap, StartPosition - 1)) {
                if (!RtlCheckBit (MmEndOfPagedPoolBitmap, StartPosition - 1)) {

                    //
                    // In the middle of an allocation... bugcheck.
                    //

                    DbgPrint("paged pool in middle of allocation\n");
                    KeBugCheck (MEMORY_MANAGEMENT);
                }
            }
        }
#endif

        i = StartPosition;
        PointerPte = MmFirstPteForPagedPool + i;

        //
        // Find the last allocated page and check to see if any
        // of the pages being deallocated are in the paging file.
        //

        while (!RtlCheckBit (MmEndOfPagedPoolBitmap, i)) {
            NumberOfPages++;
            i++;
        }

        MiDeleteSystemPagableVm (PointerPte,
                                 NumberOfPages,
                                 MM_KERNEL_DEMAND_ZERO_PTE,
                                 &PageFrameIndex);

        //
        // Clear the end of allocation bit in the bit map.
        //

        RtlClearBits (MmEndOfPagedPoolBitmap, i, 1L);
        MiReturnCommitment (NumberOfPages);
        MmPagedPoolCommit -= NumberOfPages;
        MmAllocatedPagedPool -= NumberOfPages;

        //
        // Clear the allocation bits in the bit map.
        //

        RtlClearBits (
                 MmPagedPoolAllocationMap,
                 StartPosition,
                 NumberOfPages
                 );

        MmPagedPoolHint = StartPosition;

        return NumberOfPages;
    }
}

VOID
MiInitializeNonPagedPool (
    PVOID StartOfNonPagedPool
    )

/*++

Routine Description:

    This function initializes the NonPaged pool.

    NonPaged Pool is linked together through the pages.

Arguments:

    None.

Return Value:

    None.

Environment:

    Kernel mode, during initialization.

--*/

{
    ULONG PagesInPool;
    ULONG Size;
    PMMFREE_POOL_ENTRY FreeEntry;
    PMMFREE_POOL_ENTRY FirstEntry;
    PMMPTE PointerPte;
    ULONG i;
    PULONG ThisPage;
    PULONG NextPage;

    //
    // Initialize the list head for free pages.
    //

    InitializeListHead (&MmNonPagedPoolFreeListHead);

    //
    // Initialize the must succeed pool (this occupies the first
    // pages of the pool area).
    //

    //
    // Allocate NonPage pool for the NonPagedPoolMustSucceed pool.
    //

    MmNonPagedMustSucceed = (PCHAR)MmNonPagedPoolStart;

    i = MmSizeOfNonPagedMustSucceed - PAGE_SIZE;

    MmMustSucceedPoolBitPosition = BYTES_TO_PAGES(MmSizeOfNonPagedMustSucceed);

    ThisPage = (PULONG)MmNonPagedMustSucceed;

    while (i > 0) {
        NextPage = (PULONG)((ULONG)ThisPage + PAGE_SIZE);
        *ThisPage = (ULONG)NextPage;
        ThisPage = NextPage;
        i -= PAGE_SIZE;
    }
    *ThisPage = 0;

    //
    // Set up the remaining pages as non paged pool pages.
    // NOTE - that on MIPS the initial nonpaged pool could be physical,
    // so use the NonPagedPoolStart parameter to get the virtual
    // address for building expanded pool.
    //

    ASSERT ((MmSizeOfNonPagedMustSucceed & (PAGE_SIZE - 1)) == 0);
    FreeEntry = (PMMFREE_POOL_ENTRY)((PCHAR)MmNonPagedPoolStart +
                                            MmSizeOfNonPagedMustSucceed);
    FirstEntry = FreeEntry;

    PagesInPool = BYTES_TO_PAGES(MmSizeOfNonPagedPoolInBytes -
                                    MmSizeOfNonPagedMustSucceed);

    //
    // Set the location of expanded pool.
    //

    MmExpandedPoolBitPosition = BYTES_TO_PAGES (MmSizeOfNonPagedPoolInBytes);

    MmNumberOfFreeNonPagedPool = PagesInPool;;

    InsertHeadList (&MmNonPagedPoolFreeListHead, &FreeEntry->List);

    FreeEntry->Size = PagesInPool;
#if DBG
    FreeEntry->Signature = MM_FREE_POOL_SIGNATURE;
#endif
    FreeEntry->Owner = FirstEntry;

    while (PagesInPool > 1) {
        FreeEntry = (PMMFREE_POOL_ENTRY)((PCHAR)FreeEntry + PAGE_SIZE);
#if DBG
        FreeEntry->Signature = MM_FREE_POOL_SIGNATURE;
#endif
        FreeEntry->Owner = FirstEntry;
        PagesInPool -= 1;
    }

    //
    // Set up the system PTEs for nonpaged pool expansion.
    //

    PointerPte = MiGetPteAddress (MmNonPagedPoolExpansionStart);
    ASSERT (PointerPte->u.Hard.Valid == 0);

    Size = BYTES_TO_PAGES(MmMaximumNonPagedPoolInBytes -
                            MmSizeOfNonPagedPoolInBytes) - 1;

    MiInitializeSystemPtes (PointerPte,
                            Size,
                            NonPagedPoolExpansion
                            );

    //
    // Build a guard PTE.
    //

    PointerPte += Size;
    *PointerPte = ZeroKernelPte;

    return;
}

#if DBG || (i386 && !FPO)

//
// This only works on checked builds, because the TraceLargeAllocs array is
// kept in that case to keep track of page size pool allocations.  Otherwise
// we will call ExpSnapShotPoolPages with a page size pool allocation containing
// arbitrary data and it will potentially go off in the weeds trying to interpret
// it as a suballocated pool page.  Ideally, there would be another bit map
// that identified single page pool allocations so ExpSnapShotPoolPages would NOT
// be called for those.
//

NTSTATUS
MmSnapShotPool(
    IN POOL_TYPE PoolType,
    IN PMM_SNAPSHOT_POOL_PAGE SnapShotPoolPage,
    IN PSYSTEM_POOL_INFORMATION PoolInformation,
    IN ULONG Length,
    IN OUT PULONG RequiredLength
    )
{
    NTSTATUS Status;
    NTSTATUS xStatus;
    PCHAR p, pStart;
    PVOID *pp;
    ULONG Size;
    ULONG BusyFlag;
    ULONG CurrentPage, NumberOfPages;
    PSYSTEM_POOL_ENTRY PoolEntryInfo;
    PLIST_ENTRY Entry;
    PMMFREE_POOL_ENTRY FreePageInfo;
    ULONG StartPosition;
    PMMPTE PointerPte;
    PMMPFN Pfn1;

    Status = STATUS_SUCCESS;
    PoolEntryInfo = &PoolInformation->Entries[ 0 ];
    if (PoolType == PagedPool) {
        PoolInformation->TotalSize = (ULONG)MmPagedPoolEnd -
                                     (ULONG)MmPagedPoolStart;
        PoolInformation->FirstEntry = MmPagedPoolStart;
        p = MmPagedPoolStart;
        CurrentPage = 0;
        while (p < (PCHAR)MmPagedPoolEnd) {
            pStart = p;
            BusyFlag = RtlCheckBit( MmPagedPoolAllocationMap, CurrentPage );
            while ( ~(BusyFlag ^ RtlCheckBit( MmPagedPoolAllocationMap, CurrentPage )) ) {
                p += PAGE_SIZE;
                if (RtlCheckBit( MmEndOfPagedPoolBitmap, CurrentPage )) {
                    CurrentPage++;
                    break;
                    }

                CurrentPage++;
                if (p > (PCHAR)MmPagedPoolEnd) {
                   break;
                   }
                }

            Size = p - pStart;
            if (BusyFlag) {
                xStatus = (*SnapShotPoolPage)( pStart,
                                               Size,
                                               PoolInformation,
                                               &PoolEntryInfo,
                                               Length,
                                               RequiredLength
                                             );
                if ( xStatus != STATUS_COMMITMENT_LIMIT ) {
                    Status = xStatus;
                    }
                }
            else {
                PoolInformation->NumberOfEntries += 1;
                *RequiredLength += sizeof( SYSTEM_POOL_ENTRY );
                if (Length < *RequiredLength) {
                    Status = STATUS_INFO_LENGTH_MISMATCH;
                    }
                else {
                    PoolEntryInfo->Allocated = FALSE;
                    PoolEntryInfo->Size = Size;
                    PoolEntryInfo->AllocatorBackTraceIndex = 0;
                    PoolEntryInfo->TagUlong = 0;
                    PoolEntryInfo++;
                    Status = STATUS_SUCCESS;
                    }
                }
            }
        }
    else
    if (PoolType == NonPagedPool) {
        PoolInformation->TotalSize = MmSizeOfNonPagedPoolInBytes;
        PoolInformation->FirstEntry = MmNonPagedPoolStart;

        p = MmNonPagedPoolStart;
        while (p < (PCHAR)MmNonPagedPoolEnd) {

            //
            // NonPaged pool is linked together through the pages themselves.
            //

            pp = (PVOID *)MmNonPagedMustSucceed;
            while (pp) {
                if (p == (PCHAR)pp) {
                    PoolInformation->NumberOfEntries += 1;
                    *RequiredLength += sizeof( SYSTEM_POOL_ENTRY );
                    if (Length < *RequiredLength) {
                        Status = STATUS_INFO_LENGTH_MISMATCH;
                        }
                    else {
                        PoolEntryInfo->Allocated = FALSE;
                        PoolEntryInfo->Size = PAGE_SIZE;
                        PoolEntryInfo->AllocatorBackTraceIndex = 0;
                        PoolEntryInfo->TagUlong = 0;
                        PoolEntryInfo++;
                        Status = STATUS_SUCCESS;
                        }

                    p += PAGE_SIZE;
                    pp = (PVOID *)MmNonPagedMustSucceed;
                    }
                else {
                    pp = (PVOID *)*pp;
                    }
                }

            Entry = MmNonPagedPoolFreeListHead.Flink;
            while (Entry != &MmNonPagedPoolFreeListHead) {
                FreePageInfo = CONTAINING_RECORD( Entry,
                                                  MMFREE_POOL_ENTRY,
                                                  List
                                                );

                ASSERT (FreePageInfo->Signature == MM_FREE_POOL_SIGNATURE);
                if (p == (PCHAR)FreePageInfo) {
                    Size = (FreePageInfo->Size * PAGE_SIZE);
                    PoolInformation->NumberOfEntries += 1;
                    *RequiredLength += sizeof( SYSTEM_POOL_ENTRY );
                    if (Length < *RequiredLength) {
                        Status = STATUS_INFO_LENGTH_MISMATCH;
                        }
                    else {
                        PoolEntryInfo->Allocated = FALSE;
                        PoolEntryInfo->Size = Size;
                        PoolEntryInfo->AllocatorBackTraceIndex = 0;
                        PoolEntryInfo->TagUlong = 0;
                        PoolEntryInfo++;
                        Status = STATUS_SUCCESS;
                        }

                    p += Size;
                    break;
                    }

                Entry = FreePageInfo->List.Flink;
                }

            StartPosition = BYTES_TO_PAGES((ULONG)p -
                  (ULONG)MmPageAlignedPoolBase[NonPagedPool]);
            if (StartPosition >= MmExpandedPoolBitPosition) {
                break;
                }

            if (StartPosition < MmMustSucceedPoolBitPosition) {
                Size = PAGE_SIZE;
                xStatus = (*SnapShotPoolPage)( p,
                                               Size,
                                               PoolInformation,
                                               &PoolEntryInfo,
                                               Length,
                                               RequiredLength
                                             );
                if ( xStatus != STATUS_COMMITMENT_LIMIT ) {
                    Status = xStatus;
                    }
                }
            else {
                if (MI_IS_PHYSICAL_ADDRESS(p)) {
                    //
                    // On certains architectures (e.g., MIPS) virtual addresses
                    // may be physical and hence have no corresponding PTE.
                    //
                    PointerPte = NULL;
                    Pfn1 = MI_PFN_ELEMENT (MI_CONVERT_PHYSICAL_TO_PFN (p));
                } else {
                    PointerPte = MiGetPteAddress (p);
                    Pfn1 = MI_PFN_ELEMENT (PointerPte->u.Hard.PageFrameNumber);
                }
                ASSERT (Pfn1->u3.e1.StartOfAllocation != 0);

                //
                // Find end of allocation and determine size.
                //

                NumberOfPages = 1;
                while (Pfn1->u3.e1.EndOfAllocation == 0) {
                    NumberOfPages++;
                    if (PointerPte == NULL) {
                        Pfn1 += 1;
                        }
                    else {
                        PointerPte++;
                        Pfn1 = MI_PFN_ELEMENT (PointerPte->u.Hard.PageFrameNumber);
                        }
                    }

                Size = NumberOfPages * PAGE_SIZE;
                xStatus = (*SnapShotPoolPage)( p,
                                               Size,
                                               PoolInformation,
                                               &PoolEntryInfo,
                                               Length,
                                               RequiredLength
                                             );
                if ( xStatus != STATUS_COMMITMENT_LIMIT ) {
                    Status = xStatus;
                    }
                }

            p += Size;
            }
        }
    else {
        Status = STATUS_NOT_IMPLEMENTED;
        }

    return( Status );
}


ULONG MmSpecialPoolTag;
PVOID MmSpecialPoolStart;
PVOID MmSpecialPoolEnd;
PMMPTE SpecialPoolFirstPte;
PMMPTE SpecialPoolLastPte;

VOID
MmInitializeSpecialPool (
    VOID
    )

{
    KIRQL OldIrql;
    PMMPTE pte;

    LOCK_PFN (OldIrql);
    SpecialPoolFirstPte = MiReserveSystemPtes (25000, SystemPteSpace, 0, 0, TRUE);
    UNLOCK_PFN (OldIrql);

    //
    // build list of pte pairs.
    //

    SpecialPoolLastPte = SpecialPoolFirstPte + 25000;
    MmSpecialPoolStart = MiGetVirtualAddressMappedByPte (SpecialPoolFirstPte);

    pte = SpecialPoolFirstPte;
    while (pte < SpecialPoolLastPte) {
        pte->u.List.NextEntry = ((pte+2) - MmSystemPteBase);
        pte += 2;
    }
    pte -= 2;
    pte->u.List.NextEntry = MM_EMPTY_PTE_LIST;
    SpecialPoolLastPte = pte;
    MmSpecialPoolEnd = MiGetVirtualAddressMappedByPte (SpecialPoolLastPte + 1);
}


PVOID
MmAllocateSpecialPool (
    IN ULONG NumberOfBytes,
    IN ULONG Tag
    )

{
    MMPTE TempPte;
    ULONG PageFrameIndex;
    PMMPTE PointerPte;
    KIRQL OldIrql2;
    PULONG Entry;


    TempPte = ValidKernelPte;

    LOCK_PFN2 (OldIrql2);
    if (MmAvailablePages == 0) {
        KeBugCheck (MEMORY_MANAGEMENT);
    }

    PointerPte = SpecialPoolFirstPte;

    ASSERT (SpecialPoolFirstPte->u.List.NextEntry != MM_EMPTY_PTE_LIST);

    SpecialPoolFirstPte = PointerPte->u.List.NextEntry + MmSystemPteBase;

    PageFrameIndex = MiRemoveAnyPage (MI_GET_PAGE_COLOR_FROM_PTE (PointerPte));

    TempPte.u.Hard.PageFrameNumber = PageFrameIndex;
    *PointerPte = TempPte;
    MiInitializePfn (PageFrameIndex, PointerPte, 1);
    UNLOCK_PFN2 (OldIrql2);

    Entry = (PULONG)MiGetVirtualAddressMappedByPte (PointerPte);

    Entry = (PULONG)(PVOID)(((ULONG)Entry + (PAGE_SIZE - (NumberOfBytes + 8))) &
            0xfffffff8L);

    *Entry = MmSpecialPoolTag;
    Entry += 1;
    *Entry = NumberOfBytes;
    Entry += 1;
    return (PVOID)(Entry);
}

VOID
MmFreeSpecialPool (
    IN PVOID P
    )

{
    PMMPTE PointerPte;
    PMMPFN Pfn1;
    PULONG Entry;
    KIRQL OldIrql;

    Entry = (PULONG)((PCH)P - 8);

    PointerPte = MiGetPteAddress (P);

    if (PointerPte->u.Hard.Valid == 0) {
        KeBugCheck (MEMORY_MANAGEMENT);
    }

    ASSERT (*Entry == MmSpecialPoolTag);

    KeSweepDcache(TRUE);

    Pfn1 = MI_PFN_ELEMENT (PointerPte->u.Hard.PageFrameNumber);
    MI_SET_PFN_DELETED (Pfn1);

    LOCK_PFN2 (OldIrql);
    MiDecrementShareCount (PointerPte->u.Hard.PageFrameNumber);
    KeFlushSingleTb (PAGE_ALIGN(P),
                     TRUE,
                     TRUE,
                     (PHARDWARE_PTE)PointerPte,
                     ZeroKernelPte.u.Flush);

    ASSERT (SpecialPoolLastPte->u.List.NextEntry == MM_EMPTY_PTE_LIST);
    SpecialPoolLastPte->u.List.NextEntry = PointerPte - MmSystemPteBase;

    SpecialPoolLastPte = PointerPte;
    SpecialPoolLastPte->u.List.NextEntry = MM_EMPTY_PTE_LIST;

    UNLOCK_PFN2 (OldIrql);

    return;
}

#endif // DBG || (i386 && !FPO)

